<template>
  <view class="tn-water-fall">
    <view
      id="tn-water-fall-left"
      class="tn-water-fall--column"
    >
      <slot name="left" :data="leftData"></slot>
    </view>

    <view
      id="tn-water-fall-right"
      class="tn-water-fall--column"
    >
      <slot name="right" :data="rightData"></slot>
    </view>
  </view>
</template>

<script lang="ts" setup>
import { 
  ComponentInternalInstance, 
  getCurrentInstance, 
  watch,
  ref,
onMounted
} from 'vue'
import {
  waterFallProps,
  waterFallEmits
} from '../../../types/tn-waterfall'
import {
  deepClone,
  getRectInfo
} from '../../../utils'

const instance = getCurrentInstance() as ComponentInternalInstance

const props = defineProps(waterFallProps)
const emits = defineEmits(waterFallEmits)

// 临时数据
let tempData: any[] = []
// 左半边数据
let leftData = ref<any[]>([])
// 右半边数据
let rightData = ref<any[]>([])


watch(() => props.modelValue, (nVal: any[], oVal: any) => {
  // 数据开始变化的下标位置
  const startIndex = Array.isArray(oVal) && oVal?.length > 0 ? oVal.length : 0
  tempData = tempData.concat(deepClone(nVal.slice(startIndex)))
  generateLeftRightData()
})

// 拆分数据
const generateLeftRightData = async () => {
  if (!tempData.length) return

  try {
    let leftRect = await getRectInfo('#tn-water-fall-left', instance)
    let rightRect = await getRectInfo('#tn-water-fall-right', instance)
    if (!leftRect || !rightRect) return

    // 因为经过上面两个await节点查询和定时器，数组有可能会变成空[]，导致item的值为undefined
    // 解决多次快速滚动会导致数据乱的问题
    let item = tempData[0]
    if (!item) return

    // 如果左边小于或者等于右边，就添加到左边，否则添加到右边
    const leftHeight = leftRect.height as number
    const rightHeight = rightRect.height as number
    if (leftHeight < rightHeight) {
      leftData.value.push(item)
    } else if (leftHeight > rightHeight) {
      rightData.value.push(item)
    } else {
      // 为了保证前两项添加时，左右两边都还没有内容，这时候根据队列长度判断下一项该放在哪一边
      if (leftData.value.length <= rightData.value.length) {
        leftData.value.push(item)
      } else {
        rightData.value.push(item)
      }
    }

    // 移除已经处理了的数据
    tempData.splice(0, 1)
    // 如果还有数据则继续执行
    if (tempData.length) {
      setTimeout(() => {
        generateLeftRightData()
      }, Number(props.addTime))
    } else {
      emits('finish')
    }
  } catch (e: any) {
    console.error('[tn-waterfall]获取瀑布流高度失败', e)
  }
}

// 清空列表
const clear = () => {
  leftData.value = []
  rightData.value = []
  tempData = []
  emits('update:modelValue', [])
}

// 移除指定某一条数据，传递idKey对应键的值
const _findDataIndex = (data: any[], id: any) => {
  return data.findIndex(val => val[props.idKey] === id)
}
const remove = (id: any) => {
  let index: number = -1
  // 如果index不等于-1，说明已经找到了指定的数据
  if ((index = _findDataIndex(leftData.value, id)) !== -1) {
    leftData.value.splice(index, 1)
  } else if ((index = _findDataIndex(rightData.value, id)) !== -1) {
    rightData.value.splice(index, 1)
  }
  // 同时删除父组件对应的数据
  if ((index = _findDataIndex(props.modelValue as any[], id)) !== -1) {
    emits('update:modelValue', props.modelValue.splice(index, 1))
  }
}

// 根据idKey的修改对应key对应的值
const modify = (id: any, key: string, value: any) => {
  let index = -1
  // 如果index不等于-1，说明已经找到了指定的数据
  if ((index = _findDataIndex(leftData.value, id)) !== -1) {
    leftData.value[index][key] = value
  } else if ((index = _findDataIndex(rightData.value, id)) !== -1) {
    rightData.value[index][key] = value
  }
  // 同时删除父组件对应的数据
  if ((index = _findDataIndex(props.modelValue as any[], id)) !== -1) {
    const modifyTempData: any[] = deepClone(props.modelValue) as any[]
    modifyTempData[index][key] = value
    emits('update:modelValue', modifyTempData)
  }
}

onMounted(() => {
  tempData = deepClone(props.modelValue) as any[]
  generateLeftRightData()
})

defineExpose({
  modify,
  remove,
  clear
})
</script>

<script lang="ts">
  export default {
    options: {
      // 在微信小程序中将组件节点渲染为虚拟节点，更加接近Vue组件的表现(不会出现shadow节点下再去创建元素)
      virtualHost: true
    }
  }
</script>

<style lang="scss" scoped>
  .tn-water-fall {
    display: flex;
    flex-direction: row;
    align-items: flex-start;
    width: 100%;

    &--column {
      display: flex;
      flex-direction: column;
      flex: 1;
      height: auto;
    }
    &--item {
      width: 100%;
    }
  }
</style>
